
#include "wlr-data-control-unstable-v1.h"

#include <wayland-client.h>

#include <iostream>
#include <string.h>
#include <unistd.h>

class Display
{
public:
    Display();
    ~Display();

    void roundTrip();
    void eventLoop();

private:
    void registry_handle_global(uint32_t name, const char *interface, uint32_t version);
    void registry_handle_global_remove(uint32_t name);
    void data_control_device_selection(zwlr_data_control_offer_v1 *id); // 剪切板复制内容变化
    void data_control_device_primary_selection(zwlr_data_control_offer_v1 *id); // 选中但未复制的内容变化
    void data_control_device_finished();

    void maybeInitDataControlDevice();

    wl_display *m_display = nullptr;
    wl_registry *m_registry = nullptr;
    wl_registry_listener m_registry_listener;
    zwlr_data_control_device_v1_listener m_data_control_device_listener;

    // wayland objects
    wl_seat *m_seat = nullptr;

    // wlr data control objects
    zwlr_data_control_manager_v1 *m_data_control_manager = nullptr;
    zwlr_data_control_device_v1 *m_data_control_device = nullptr;
};

Display::Display()
{
    m_display = wl_display_connect(NULL);
    if (m_display == NULL) {
        std::cerr << "Failed to connect to Wayland display" << std::endl;
        exit(1);
    }
    m_registry = wl_display_get_registry(m_display);
    if (m_registry == NULL) {
        std::cerr << "Failed to get Wayland registry" << std::endl;
        exit(1);
    }
    m_registry_listener.global = [](void *data, struct wl_registry *wl_registry, uint32_t name, const char *interface, uint32_t version) {
        static_cast<Display *>(data)->registry_handle_global(name, interface, version);
    };

    m_registry_listener.global_remove = [](void *data, struct wl_registry *wl_registry, uint32_t name) {
        static_cast<Display *>(data)->registry_handle_global_remove(name);
    };

    m_data_control_device_listener.finished = [](void *data, zwlr_data_control_device_v1 *zwlr_data_control_device_v1) {
        static_cast<Display *>(data)->data_control_device_finished();
    };

    m_data_control_device_listener.selection = [](void *data, zwlr_data_control_device_v1 *zwlr_data_control_device_v1, struct zwlr_data_control_offer_v1 *id) {
        static_cast<Display *>(data)->data_control_device_selection(id);
    };

    m_data_control_device_listener.primary_selection = [](void *data, zwlr_data_control_device_v1 *zwlr_data_control_device_v1, zwlr_data_control_offer_v1 *id) {
        static_cast<Display *>(data)->data_control_device_primary_selection(id);
    };

    m_data_control_device_listener.data_offer = [](void *data, zwlr_data_control_device_v1 *zwlr_data_control_device_v1, struct zwlr_data_control_offer_v1 *id) {
        // Do nothing
    };

    wl_registry_add_listener(m_registry, &m_registry_listener, this);
    // This is the first roundtrip to get the globals
    wl_display_roundtrip(m_display);

    if (m_data_control_manager == nullptr) {
        std::cerr << "Compositor does not support wlr-data-control-unstable-v1" << std::endl;
        exit(1);
    }
}

Display::~Display()
{
    wl_registry_destroy(m_registry);
    wl_display_disconnect(m_display);
}

void Display::roundTrip()
{
    wl_display_roundtrip(m_display);
}

void Display::eventLoop()
{
    while (wl_display_dispatch(m_display) != -1) {
        // Do nothing
    }
}

void Display::registry_handle_global(uint32_t name, const char *interface, uint32_t version)
{
    if (strcmp(interface, "wl_seat") == 0) {
        m_seat = static_cast<wl_seat *>(wl_registry_bind(m_registry, name, &wl_seat_interface, version));
        maybeInitDataControlDevice();
    } else if (strcmp(interface, "zwlr_data_control_manager_v1") == 0) {
        m_data_control_manager = static_cast<zwlr_data_control_manager_v1 *>(wl_registry_bind(m_registry, name, &zwlr_data_control_manager_v1_interface, version));
        maybeInitDataControlDevice();
    }
}

void Display::registry_handle_global_remove(uint32_t name)
{
    std::cout << "Got global remove: " << name << std::endl;
}

void Display::data_control_device_selection(zwlr_data_control_offer_v1 *id)
{
    if (!id)
        return;
    int fd[2];
    if (pipe(fd) == -1) {
        std::cerr << "Failed to create pipe" << std::endl;
        return;
    }
    zwlr_data_control_offer_v1_receive(id, "text/plain", fd[1]);
    wl_display_flush(m_display);

    close(fd[1]);
    char buf[1024];
    ssize_t len = read(fd[0], buf, sizeof(buf));
    if (len == -1) {
        std::cerr << "Failed to read from pipe" << std::endl;
        return;
    }
    buf[len] = '\0';
    std::cout << "selection data changed: " << buf << std::endl;
    zwlr_data_control_offer_v1_destroy(id);
}

void Display::data_control_device_finished()
{
    zwlr_data_control_device_v1_destroy(m_data_control_device);
    m_data_control_device = nullptr;
}

void Display::data_control_device_primary_selection(zwlr_data_control_offer_v1 *id)
{
    if (!id)
        return;
    int fd[2];
    if (pipe(fd) == -1) {
        std::cerr << "Failed to create pipe" << std::endl;
        return;
    }
    zwlr_data_control_offer_v1_receive(id, "text/plain", fd[1]);
    wl_display_flush(m_display);

    close(fd[1]);
    char buf[1024];
    ssize_t len = read(fd[0], buf, sizeof(buf));
    if (len == -1) {
        std::cerr << "Failed to read from pipe" << std::endl;
        return;
    }
    buf[len] = '\0';
    std::cout << "Primary selection data changed: " << buf << std::endl;
    zwlr_data_control_offer_v1_destroy(id);
}

void Display::maybeInitDataControlDevice()
{
    if (m_seat && m_data_control_manager != nullptr) {
        m_data_control_device = zwlr_data_control_manager_v1_get_data_device(m_data_control_manager, m_seat);
        zwlr_data_control_device_v1_add_listener(m_data_control_device, &m_data_control_device_listener, this);
        wl_display_roundtrip(m_display);
    }
}

int main()
{
    Display display;
    display.eventLoop();
    return 0;
}